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 char *get_context_comm(const struct bt_ctf_event
*event
)
89 const struct bt_definition
*scope
;
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
,
95 if (bt_ctf_field_get_error()) {
96 fprintf(stderr
, "Missing comm context info\n");
104 * To get the parent process, put the pid in the tid field
105 * because the parent process gets pid = tid
107 * FIXME : char *comm useful ???
109 struct processtop
*find_process_tid(struct lttngtop
*ctx
, int tid
, char *comm
)
112 struct processtop
*tmp
;
114 for (i
= 0; i
< ctx
->process_table
->len
; i
++) {
115 tmp
= g_ptr_array_index(ctx
->process_table
, i
);
116 if (tmp
&& tmp
->tid
== tid
)
122 struct processtop
* add_proc(struct lttngtop
*ctx
, int tid
, char *comm
,
123 unsigned long timestamp
)
125 struct processtop
*newproc
;
127 /* if the PID already exists, we just rename the process */
128 /* FIXME : need to integrate with clone/fork/exit to be accurate */
129 newproc
= find_process_tid(ctx
, tid
, comm
);
131 newproc
= g_new0(struct processtop
, 1);
133 newproc
->birth
= timestamp
;
134 newproc
->process_files_table
= g_ptr_array_new();
135 newproc
->files_history
= NULL
;
136 newproc
->totalfileread
= 0;
137 newproc
->totalfilewrite
= 0;
138 newproc
->fileread
= 0;
139 newproc
->filewrite
= 0;
140 newproc
->syscall_info
= NULL
;
141 newproc
->threadparent
= NULL
;
142 newproc
->threads
= g_ptr_array_new();
143 newproc
->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
144 g_ptr_array_add(ctx
->process_table
, newproc
);
149 newproc
->comm
= strdup(comm
);
154 struct processtop
* update_proc(struct processtop
* proc
, int pid
, int tid
,
155 int ppid
, char *comm
)
161 if (strcmp(proc
->comm
, comm
) != 0) {
163 proc
->comm
= strdup(comm
);
170 * This function just sets the time of death of a process.
171 * When we rotate the cputime we remove it from the process list.
173 void death_proc(struct lttngtop
*ctx
, int tid
, char *comm
,
174 unsigned long timestamp
)
176 struct processtop
*tmp
;
177 tmp
= find_process_tid(ctx
, tid
, comm
);
178 if (tmp
&& strcmp(tmp
->comm
, comm
) == 0) {
179 tmp
->death
= timestamp
;
180 ctx
->nbdeadthreads
++;
185 struct processtop
* get_proc(struct lttngtop
*ctx
, int tid
, char *comm
,
186 unsigned long timestamp
)
188 struct processtop
*tmp
;
189 tmp
= find_process_tid(ctx
, tid
, comm
);
190 if (tmp
&& strcmp(tmp
->comm
, comm
) == 0)
192 return add_proc(ctx
, tid
, comm
, timestamp
);
195 struct processtop
*get_proc_pid(struct lttngtop
*ctx
, int tid
, int pid
,
196 unsigned long timestamp
)
198 struct processtop
*tmp
;
199 tmp
= find_process_tid(ctx
, tid
, NULL
);
200 if (tmp
&& tmp
->pid
== pid
)
202 return add_proc(ctx
, tid
, "Unknown", timestamp
);
205 void add_thread(struct processtop
*parent
, struct processtop
*thread
)
208 struct processtop
*tmp
;
210 for (i
= 0; i
< parent
->threads
->len
; i
++) {
211 tmp
= g_ptr_array_index(parent
->threads
, i
);
215 g_ptr_array_add(parent
->threads
, thread
);
218 struct cputime
* add_cpu(int cpu
)
220 struct cputime
*newcpu
;
222 newcpu
= g_new0(struct cputime
, 1);
224 newcpu
->current_task
= NULL
;
225 newcpu
->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
227 g_ptr_array_add(lttngtop
.cpu_table
, newcpu
);
231 struct cputime
* get_cpu(int cpu
)
236 for (i
= 0; i
< lttngtop
.cpu_table
->len
; i
++) {
237 tmp
= g_ptr_array_index(lttngtop
.cpu_table
, i
);
246 * At the end of a sampling period, we need to display the cpu time for each
247 * process and to reset it to zero for the next period
249 void rotate_cputime(unsigned long end
)
253 unsigned long elapsed
;
255 for (i
= 0; i
< lttngtop
.cpu_table
->len
; i
++) {
256 tmp
= g_ptr_array_index(lttngtop
.cpu_table
, i
);
257 elapsed
= end
- tmp
->task_start
;
258 if (tmp
->current_task
) {
259 tmp
->current_task
->totalcpunsec
+= elapsed
;
260 tmp
->current_task
->threadstotalcpunsec
+= elapsed
;
261 if (tmp
->current_task
->pid
!= tmp
->current_task
->tid
&&
262 tmp
->current_task
->threadparent
) {
263 tmp
->current_task
->threadparent
->threadstotalcpunsec
+= elapsed
;
266 tmp
->task_start
= end
;
270 void reset_perf_counter(gpointer key
, gpointer value
, gpointer user_data
)
272 ((struct perfcounter
*) value
)->count
= 0;
275 void copy_perf_counter(gpointer key
, gpointer value
, gpointer new_table
)
277 struct perfcounter
*newperf
;
279 newperf
= g_new0(struct perfcounter
, 1);
280 newperf
->count
= ((struct perfcounter
*) value
)->count
;
281 newperf
->visible
= ((struct perfcounter
*) value
)->visible
;
282 newperf
->sort
= ((struct perfcounter
*) value
)->sort
;
283 g_hash_table_insert((GHashTable
*) new_table
, strdup(key
), newperf
);
286 void rotate_perfcounter() {
288 struct processtop
*tmp
;
289 for (i
= 0; i
< lttngtop
.process_table
->len
; i
++) {
290 tmp
= g_ptr_array_index(lttngtop
.process_table
, i
);
291 g_hash_table_foreach(tmp
->perf
, reset_perf_counter
, NULL
);
295 void cleanup_processtop()
298 struct processtop
*tmp
;
299 struct files
*tmpf
; /* a temporary file */
301 for (i
= 0; i
< lttngtop
.process_table
->len
; i
++) {
302 tmp
= g_ptr_array_index(lttngtop
.process_table
, i
);
303 tmp
->totalcpunsec
= 0;
304 tmp
->threadstotalcpunsec
= 0;
308 for (j
= 0; j
< tmp
->process_files_table
->len
; j
++) {
309 tmpf
= g_ptr_array_index(tmp
->process_files_table
, j
);
314 if (tmpf
->flag
== __NR_close
)
316 tmp
->process_files_table
, j
323 void reset_global_counters()
325 lttngtop
.nbnewproc
= 0;
326 lttngtop
.nbdeadproc
= 0;
327 lttngtop
.nbnewthreads
= 0;
328 lttngtop
.nbdeadthreads
= 0;
329 lttngtop
.nbnewfiles
= 0;
330 lttngtop
.nbclosedfiles
= 0;
333 void copy_global_counters(struct lttngtop
*dst
)
335 dst
->nbproc
= lttngtop
.nbproc
;
336 dst
->nbnewproc
= lttngtop
.nbnewproc
;
337 dst
->nbdeadproc
= lttngtop
.nbdeadproc
;
338 dst
->nbthreads
= lttngtop
.nbthreads
;
339 dst
->nbnewthreads
= lttngtop
.nbnewthreads
;
340 dst
->nbdeadthreads
= lttngtop
.nbdeadthreads
;
341 dst
->nbfiles
= lttngtop
.nbfiles
;
342 dst
->nbnewfiles
= lttngtop
.nbnewfiles
;
343 dst
->nbclosedfiles
= lttngtop
.nbclosedfiles
;
344 reset_global_counters();
347 struct lttngtop
* get_copy_lttngtop(unsigned long start
, unsigned long end
)
351 struct lttngtop
*dst
;
352 struct processtop
*tmp
, *tmp2
, *new;
353 struct cputime
*tmpcpu
, *newcpu
;
354 struct files
*tmpfile
, *newfile
;
356 dst
= g_new0(struct lttngtop
, 1);
359 copy_global_counters(dst
);
360 dst
->process_table
= g_ptr_array_new();
361 dst
->files_table
= g_ptr_array_new();
362 dst
->cpu_table
= g_ptr_array_new();
366 for (i
= 0; i
< lttngtop
.process_table
->len
; i
++) {
367 tmp
= g_ptr_array_index(lttngtop
.process_table
, i
);
368 new = g_new0(struct processtop
, 1);
370 memcpy(new, tmp
, sizeof(struct processtop
));
371 new->threads
= g_ptr_array_new();
372 new->comm
= strdup(tmp
->comm
);
373 new->process_files_table
= g_ptr_array_new();
374 new->files_history
= tmp
->files_history
;
375 new->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
376 g_hash_table_foreach(tmp
->perf
, copy_perf_counter
, new->perf
);
378 /* compute the stream speed */
379 if (end
- start
!= 0) {
380 time
= (end
- start
) / NSEC_PER_SEC
;
381 new->fileread
= new->fileread
/(time
);
382 new->filewrite
= new->filewrite
/(time
);
385 for (j
= 0; j
< tmp
->process_files_table
->len
; j
++) {
386 tmpfile
= g_ptr_array_index(tmp
->process_files_table
, j
);
388 newfile
= malloc(sizeof(struct files
));
390 if (tmpfile
!= NULL
) {
391 memcpy(newfile
, tmpfile
, sizeof(struct files
));
392 newfile
->name
= strdup(tmpfile
->name
);
394 g_ptr_array_add(new->process_files_table
,
396 g_ptr_array_add(dst
->files_table
, newfile
);
398 g_ptr_array_add(new->process_files_table
, NULL
);
399 g_ptr_array_add(dst
->files_table
, NULL
);
402 * if the process died during the last period, we remove all
403 * files associated with if after the copy
405 if (tmp
->death
> 0 && tmp
->death
< end
) {
406 /* FIXME : close the files before */
407 g_ptr_array_remove(tmp
->process_files_table
, tmpfile
);
411 g_ptr_array_add(dst
->process_table
, new);
414 * if the process died during the last period, we remove it from
415 * the current process list after the copy
417 if (tmp
->death
> 0 && tmp
->death
< end
) {
418 g_ptr_array_remove(lttngtop
.process_table
, tmp
);
419 /* FIXME : TRUE does not mean clears the object in it */
420 g_ptr_array_free(tmp
->threads
, TRUE
);
422 g_ptr_array_free(tmp
->process_files_table
, TRUE
);
423 /* FIXME : clear elements */
424 g_hash_table_destroy(tmp
->perf
);
428 rotate_perfcounter();
430 for (i
= 0; i
< lttngtop
.cpu_table
->len
; i
++) {
431 tmpcpu
= g_ptr_array_index(lttngtop
.cpu_table
, i
);
432 newcpu
= g_new0(struct cputime
, 1);
433 memcpy(newcpu
, tmpcpu
, sizeof(struct cputime
));
434 newcpu
->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
435 g_hash_table_foreach(tmpcpu
->perf
, copy_perf_counter
, newcpu
->perf
);
437 * note : we don't care about the current process pointer in the copy
438 * so the reference is invalid after the memcpy
440 g_ptr_array_add(dst
->cpu_table
, newcpu
);
442 /* FIXME : better algo */
443 /* create the threads index if required */
444 for (i
= 0; i
< dst
->process_table
->len
; i
++) {
445 tmp
= g_ptr_array_index(dst
->process_table
, i
);
446 if (tmp
->pid
== tmp
->tid
) {
447 for (j
= 0; j
< dst
->process_table
->len
; j
++) {
448 tmp2
= g_ptr_array_index(dst
->process_table
, j
);
449 if (tmp2
->pid
== tmp
->pid
) {
450 tmp2
->threadparent
= tmp
;
451 g_ptr_array_add(tmp
->threads
, tmp2
);
457 // update_global_stats(dst);
458 cleanup_processtop();
464 enum bt_cb_ret
handle_statedump_process_state(struct bt_ctf_event
*call_data
,
467 const struct bt_definition
*scope
;
468 struct processtop
*proc
;
469 unsigned long timestamp
;
473 timestamp
= bt_ctf_get_timestamp(call_data
);
474 if (timestamp
== -1ULL)
477 scope
= bt_ctf_get_top_level_scope(call_data
,
479 pid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
481 if (bt_ctf_field_get_error()) {
482 fprintf(stderr
, "Missing pid context info\n");
486 scope
= bt_ctf_get_top_level_scope(call_data
,
488 tid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
490 if (bt_ctf_field_get_error()) {
491 fprintf(stderr
, "Missing tid context info\n");
497 * I first tried with bt_ctf_get_string but doesn`t work at all
498 * It couldn`t find the field _name because it is an integer in
499 * the metadata and not a string like _filename for the
500 * statedump_file_descriptor
502 scope
= bt_ctf_get_top_level_scope(call_data
,
504 procname
= bt_ctf_get_char_array(bt_ctf_get_field(call_data
,
506 if (bt_ctf_field_get_error()) {
507 fprintf(stderr
, "Missing process name context info\n");
511 proc
= find_process_tid(<tngtop
, tid
, procname
);
513 proc
= add_proc(<tngtop
, tid
, procname
, timestamp
);
516 proc
->comm
= strdup(procname
);
521 * I would like to free procname because it is duplicated
522 * when the process is created but it segfaults...
530 return BT_CB_ERROR_STOP
;
533 struct tm
format_timestamp(uint64_t timestamp
)
536 uint64_t ts_sec
= 0, ts_nsec
;
540 ts_sec
+= ts_nsec
/ NSEC_PER_SEC
;
541 ts_nsec
= ts_nsec
% NSEC_PER_SEC
;
543 time_s
= (time_t) ts_sec
;
545 localtime_r(&time_s
, &tm
);
This page took 0.043509 seconds and 4 git commands to generate.