wrapper to access context fields
[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
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
16 * MA 02111-1307, USA.
17 */
18
19 #include <babeltrace/ctf/events.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include "common.h"
23
24 uint64_t get_cpu_id(struct bt_ctf_event *event)
25 {
26 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(struct bt_ctf_event *event)
40 {
41 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(struct bt_ctf_event *event)
56 {
57 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(struct bt_ctf_event *event)
72 {
73 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(struct bt_ctf_event *event)
88 {
89 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 = g_new0(struct file_history, 1);
130 newproc->files_history->next = NULL;
131 newproc->totalfileread = 0;
132 newproc->totalfilewrite = 0;
133 newproc->fileread = 0;
134 newproc->filewrite = 0;
135 newproc->syscall_info = NULL;
136 newproc->threads = g_ptr_array_new();
137 newproc->perf = g_hash_table_new(g_str_hash, g_str_equal);
138 g_ptr_array_add(ctx->process_table, newproc);
139 }
140 newproc->comm = strdup(comm);
141
142 return newproc;
143 }
144
145 struct processtop* update_proc(struct processtop* proc, int pid, int tid,
146 int ppid, char *comm)
147 {
148 if (proc) {
149 proc->pid = pid;
150 proc->tid = tid;
151 proc->ppid = ppid;
152 if (strcmp(proc->comm, comm) != 0) {
153 free(proc->comm);
154 proc->comm = strdup(comm);
155 }
156 }
157 return proc;
158 }
159
160 /*
161 * This function just sets the time of death of a process.
162 * When we rotate the cputime we remove it from the process list.
163 */
164 void death_proc(struct lttngtop *ctx, int tid, char *comm,
165 unsigned long timestamp)
166 {
167 struct processtop *tmp;
168 tmp = find_process_tid(ctx, tid, comm);
169 if (tmp && strcmp(tmp->comm, comm) == 0)
170 tmp->death = timestamp;
171 }
172
173 struct processtop* get_proc(struct lttngtop *ctx, int tid, char *comm,
174 unsigned long timestamp)
175 {
176 struct processtop *tmp;
177 tmp = find_process_tid(ctx, tid, comm);
178 if (tmp && strcmp(tmp->comm, comm) == 0)
179 return tmp;
180 return add_proc(ctx, tid, comm, timestamp);
181 }
182
183 void add_thread(struct processtop *parent, struct processtop *thread)
184 {
185 gint i;
186 struct processtop *tmp;
187
188 for (i = 0; i < parent->threads->len; i++) {
189 tmp = g_ptr_array_index(parent->threads, i);
190 if (tmp == thread)
191 return;
192 }
193 g_ptr_array_add(parent->threads, thread);
194 }
195
196 struct cputime* add_cpu(int cpu)
197 {
198 struct cputime *newcpu;
199
200 newcpu = g_new0(struct cputime, 1);
201 newcpu->id = cpu;
202 newcpu->current_task = NULL;
203 newcpu->perf = g_hash_table_new(g_str_hash, g_str_equal);
204
205 g_ptr_array_add(lttngtop.cpu_table, newcpu);
206
207 return newcpu;
208 }
209 struct cputime* get_cpu(int cpu)
210 {
211 gint i;
212 struct cputime *tmp;
213
214 for (i = 0; i < lttngtop.cpu_table->len; i++) {
215 tmp = g_ptr_array_index(lttngtop.cpu_table, i);
216 if (tmp->id == cpu)
217 return tmp;
218 }
219
220 return add_cpu(cpu);
221 }
222
223 /*
224 * At the end of a sampling period, we need to display the cpu time for each
225 * process and to reset it to zero for the next period
226 */
227 void rotate_cputime(unsigned long end)
228 {
229 gint i;
230 struct cputime *tmp;
231 unsigned long elapsed;
232
233 for (i = 0; i < lttngtop.cpu_table->len; i++) {
234 tmp = g_ptr_array_index(lttngtop.cpu_table, i);
235 elapsed = end - tmp->task_start;
236 if (tmp->current_task) {
237 tmp->current_task->totalcpunsec += elapsed;
238 tmp->current_task->threadstotalcpunsec += elapsed;
239 if (tmp->current_task->pid != tmp->current_task->tid &&
240 tmp->current_task->threadparent) {
241 tmp->current_task->threadparent->threadstotalcpunsec += elapsed;
242 }
243 }
244 tmp->task_start = end;
245 }
246 }
247
248 void reset_perf_counter(gpointer key, gpointer value, gpointer user_data)
249 {
250 ((struct perfcounter*) value)->count = 0;
251 }
252
253 void copy_perf_counter(gpointer key, gpointer value, gpointer new_table)
254 {
255 struct perfcounter *newperf;
256
257 newperf = g_new0(struct perfcounter, 1);
258 newperf->count = ((struct perfcounter *) value)->count;
259 newperf->visible = ((struct perfcounter *) value)->visible;
260 newperf->sort = ((struct perfcounter *) value)->sort;
261 g_hash_table_insert((GHashTable *) new_table, strdup(key), newperf);
262 }
263
264 void rotate_perfcounter() {
265 int i;
266 struct processtop *tmp;
267 for (i = 0; i < lttngtop.process_table->len; i++) {
268 tmp = g_ptr_array_index(lttngtop.process_table, i);
269 g_hash_table_foreach(tmp->perf, reset_perf_counter, NULL);
270 }
271 }
272
273 void cleanup_processtop()
274 {
275 gint i, j;
276 struct processtop *tmp;
277 struct files *tmpf; /* a temporary file */
278
279 for (i = 0; i < lttngtop.process_table->len; i++) {
280 tmp = g_ptr_array_index(lttngtop.process_table, i);
281 tmp->totalcpunsec = 0;
282 tmp->threadstotalcpunsec = 0;
283 tmp->fileread = 0;
284 tmp->filewrite = 0;
285
286 for (j = 0; j < tmp->process_files_table->len; j++) {
287 tmpf = g_ptr_array_index(tmp->process_files_table, j);
288 if (tmpf != NULL) {
289 tmpf->read = 0;
290 tmpf->write = 0;
291 }
292 }
293 }
294 }
295
296 struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end)
297 {
298 gint i, j;
299 unsigned long time;
300 struct lttngtop *dst;
301 struct processtop *tmp, *tmp2, *new;
302 struct cputime *tmpcpu, *newcpu;
303 struct files *tmpfile, *newfile;
304
305 dst = g_new0(struct lttngtop, 1);
306 dst->start = start;
307 dst->end = end;
308 dst->process_table = g_ptr_array_new();
309 dst->files_table = g_ptr_array_new();
310 dst->cpu_table = g_ptr_array_new();
311 dst->perf_list = g_hash_table_new(g_str_hash, g_str_equal);
312
313 rotate_cputime(end);
314
315 g_hash_table_foreach(lttngtop.perf_list, copy_perf_counter, dst->perf_list);
316 for (i = 0; i < lttngtop.process_table->len; i++) {
317 tmp = g_ptr_array_index(lttngtop.process_table, i);
318 new = g_new0(struct processtop, 1);
319
320 memcpy(new, tmp, sizeof(struct processtop));
321 new->threads = g_ptr_array_new();
322 new->comm = strdup(tmp->comm);
323 new->process_files_table = g_ptr_array_new();
324 new->perf = g_hash_table_new(g_str_hash, g_str_equal);
325 g_hash_table_foreach(tmp->perf, copy_perf_counter, new->perf);
326
327 /* compute the stream speed */
328 if (end - start != 0) {
329 time = (end - start) / NSEC_PER_SEC;
330 new->fileread = new->fileread/(time);
331 new->filewrite = new->filewrite/(time);
332 }
333
334 for (j = 0; j < tmp->process_files_table->len; j++) {
335 tmpfile = g_ptr_array_index(tmp->process_files_table, j);
336
337 newfile = malloc(sizeof(struct files));
338
339 if (tmpfile != NULL) {
340 memcpy(newfile, tmpfile, sizeof(struct files));
341 newfile->name = strdup(tmpfile->name);
342 newfile->ref = new;
343 g_ptr_array_add(new->process_files_table,
344 newfile);
345 g_ptr_array_add(dst->files_table, newfile);
346 } else {
347 g_ptr_array_add(new->process_files_table, NULL);
348 g_ptr_array_add(dst->files_table, NULL);
349 }
350 /*
351 * if the process died during the last period, we remove all
352 * files associated with if after the copy
353 */
354 if (tmp->death > 0 && tmp->death < end) {
355 g_ptr_array_remove(tmp->process_files_table, tmpfile);
356 g_free(tmpfile);
357 }
358 }
359 g_ptr_array_add(dst->process_table, new);
360
361 /*
362 * if the process died during the last period, we remove it from
363 * the current process list after the copy
364 */
365 if (tmp->death > 0 && tmp->death < end) {
366 g_ptr_array_remove(lttngtop.process_table, tmp);
367 /* FIXME : TRUE does not mean clears the object in it */
368 g_ptr_array_free(tmp->threads, TRUE);
369 free(tmp->comm);
370 g_ptr_array_free(tmp->process_files_table, TRUE);
371 /* FIXME : clear elements */
372 g_hash_table_destroy(tmp->perf);
373 g_free(tmp);
374 }
375 }
376 rotate_perfcounter();
377
378 for (i = 0; i < lttngtop.cpu_table->len; i++) {
379 tmpcpu = g_ptr_array_index(lttngtop.cpu_table, i);
380 newcpu = g_new0(struct cputime, 1);
381 memcpy(newcpu, tmpcpu, sizeof(struct cputime));
382 newcpu->perf = g_hash_table_new(g_str_hash, g_str_equal);
383 g_hash_table_foreach(tmpcpu->perf, copy_perf_counter, newcpu->perf);
384 /*
385 * note : we don't care about the current process pointer in the copy
386 * so the reference is invalid after the memcpy
387 */
388 g_ptr_array_add(dst->cpu_table, newcpu);
389 }
390 /* FIXME : better algo */
391 /* create the threads index if required */
392 for (i = 0; i < dst->process_table->len; i++) {
393 tmp = g_ptr_array_index(dst->process_table, i);
394 if (tmp->pid == tmp->tid) {
395 for (j = 0; j < dst->process_table->len; j++) {
396 tmp2 = g_ptr_array_index(dst->process_table, j);
397 if (tmp2->pid == tmp->pid) {
398 tmp2->threadparent = tmp;
399 g_ptr_array_add(tmp->threads, tmp2);
400 }
401 }
402 }
403 }
404
405 // update_global_stats(dst);
406 cleanup_processtop();
407
408 return dst;
409 }
410
This page took 0.066446 seconds and 5 git commands to generate.