fix perf name (now without underscore prefix)
[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 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 <string.h>
21 #include "common.h"
22
23 uint64_t get_cpu_id(struct bt_ctf_event *event)
24 {
25 struct definition *scope;
26 uint64_t cpu_id;
27
28 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT);
29 cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(event, scope, "cpu_id"));
30 if (bt_ctf_field_get_error()) {
31 fprintf(stderr, "[error] get cpu_id\n");
32 return -1ULL;
33 }
34
35 return cpu_id;
36 }
37
38 uint64_t get_context_tid(struct bt_ctf_event *event)
39 {
40 struct definition *scope;
41 uint64_t tid;
42
43 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
44 tid = bt_ctf_get_int64(bt_ctf_get_field(event,
45 scope, "_tid"));
46 if (bt_ctf_field_get_error()) {
47 fprintf(stderr, "Missing tid context info\n");
48 return -1ULL;
49 }
50
51 return tid;
52 }
53
54 uint64_t get_context_pid(struct bt_ctf_event *event)
55 {
56 struct definition *scope;
57 uint64_t pid;
58
59 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
60 pid = bt_ctf_get_int64(bt_ctf_get_field(event,
61 scope, "_pid"));
62 if (bt_ctf_field_get_error()) {
63 fprintf(stderr, "Missing pid context info\n");
64 return -1ULL;
65 }
66
67 return pid;
68 }
69
70 uint64_t get_context_ppid(struct bt_ctf_event *event)
71 {
72 struct definition *scope;
73 uint64_t ppid;
74
75 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
76 ppid = bt_ctf_get_int64(bt_ctf_get_field(event,
77 scope, "_ppid"));
78 if (bt_ctf_field_get_error()) {
79 fprintf(stderr, "Missing ppid context info\n");
80 return -1ULL;
81 }
82
83 return ppid;
84 }
85
86 char *get_context_comm(struct bt_ctf_event *event)
87 {
88 struct definition *scope;
89 char *comm;
90
91 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
92 comm = bt_ctf_get_char_array(bt_ctf_get_field(event,
93 scope, "_procname"));
94 if (bt_ctf_field_get_error()) {
95 fprintf(stderr, "Missing comm context info\n");
96 return NULL;
97 }
98
99 return comm;
100 }
101
102 struct processtop *find_process_tid(struct lttngtop *ctx, int tid, char *comm)
103 {
104 gint i;
105 struct processtop *tmp;
106
107 for (i = 0; i < ctx->process_table->len; i++) {
108 tmp = g_ptr_array_index(ctx->process_table, i);
109 if (tmp && tmp->tid == tid)
110 return tmp;
111 }
112 return NULL;
113 }
114
115 struct processtop* add_proc(struct lttngtop *ctx, int tid, char *comm,
116 unsigned long timestamp)
117 {
118 struct processtop *newproc;
119
120 /* if the PID already exists, we just rename the process */
121 /* FIXME : need to integrate with clone/fork/exit to be accurate */
122 newproc = find_process_tid(ctx, tid, comm);
123 if (!newproc) {
124 newproc = g_new0(struct processtop, 1);
125 newproc->tid = tid;
126 newproc->birth = timestamp;
127 newproc->process_files_table = g_ptr_array_new();
128 newproc->files_history = g_new0(struct file_history, 1);
129 newproc->files_history->next = NULL;
130 newproc->totalfileread = 0;
131 newproc->totalfilewrite = 0;
132 newproc->fileread = 0;
133 newproc->filewrite = 0;
134 newproc->syscall_info = NULL;
135 newproc->threads = g_ptr_array_new();
136 newproc->perf = g_hash_table_new(g_str_hash, g_str_equal);
137 g_ptr_array_add(ctx->process_table, newproc);
138 }
139 newproc->comm = strdup(comm);
140
141 return newproc;
142 }
143
144 struct processtop* update_proc(struct processtop* proc, int pid, int tid,
145 int ppid, char *comm)
146 {
147 if (proc) {
148 proc->pid = pid;
149 proc->tid = tid;
150 proc->ppid = ppid;
151 if (strcmp(proc->comm, comm) != 0) {
152 free(proc->comm);
153 proc->comm = strdup(comm);
154 }
155 }
156 return proc;
157 }
158
159 /*
160 * This function just sets the time of death of a process.
161 * When we rotate the cputime we remove it from the process list.
162 */
163 void death_proc(struct lttngtop *ctx, int tid, char *comm,
164 unsigned long timestamp)
165 {
166 struct processtop *tmp;
167 tmp = find_process_tid(ctx, tid, comm);
168 if (tmp && strcmp(tmp->comm, comm) == 0)
169 tmp->death = timestamp;
170 }
171
172 struct processtop* get_proc(struct lttngtop *ctx, int tid, char *comm,
173 unsigned long timestamp)
174 {
175 struct processtop *tmp;
176 tmp = find_process_tid(ctx, tid, comm);
177 if (tmp && strcmp(tmp->comm, comm) == 0)
178 return tmp;
179 return add_proc(ctx, tid, comm, timestamp);
180 }
181
182 void add_thread(struct processtop *parent, struct processtop *thread)
183 {
184 gint i;
185 struct processtop *tmp;
186
187 for (i = 0; i < parent->threads->len; i++) {
188 tmp = g_ptr_array_index(parent->threads, i);
189 if (tmp == thread)
190 return;
191 }
192 g_ptr_array_add(parent->threads, thread);
193 }
194
195 struct cputime* add_cpu(int cpu)
196 {
197 struct cputime *newcpu;
198
199 newcpu = g_new0(struct cputime, 1);
200 newcpu->id = cpu;
201 newcpu->current_task = NULL;
202 newcpu->perf = g_hash_table_new(g_str_hash, g_str_equal);
203
204 g_ptr_array_add(lttngtop.cpu_table, newcpu);
205
206 return newcpu;
207 }
208 struct cputime* get_cpu(int cpu)
209 {
210 gint i;
211 struct cputime *tmp;
212
213 for (i = 0; i < lttngtop.cpu_table->len; i++) {
214 tmp = g_ptr_array_index(lttngtop.cpu_table, i);
215 if (tmp->id == cpu)
216 return tmp;
217 }
218
219 return add_cpu(cpu);
220 }
221
222 /*
223 * At the end of a sampling period, we need to display the cpu time for each
224 * process and to reset it to zero for the next period
225 */
226 void rotate_cputime(unsigned long end)
227 {
228 gint i;
229 struct cputime *tmp;
230 unsigned long elapsed;
231
232 for (i = 0; i < lttngtop.cpu_table->len; i++) {
233 tmp = g_ptr_array_index(lttngtop.cpu_table, i);
234 elapsed = end - tmp->task_start;
235 if (tmp->current_task) {
236 tmp->current_task->totalcpunsec += elapsed;
237 tmp->current_task->threadstotalcpunsec += elapsed;
238 if (tmp->current_task->pid != tmp->current_task->tid &&
239 tmp->current_task->threadparent) {
240 tmp->current_task->threadparent->threadstotalcpunsec += elapsed;
241 }
242 }
243 tmp->task_start = end;
244 }
245 }
246
247 void reset_perf_counter(gpointer key, gpointer value, gpointer user_data)
248 {
249 ((struct perfcounter*) value)->count = 0;
250 }
251
252 void copy_perf_counter(gpointer key, gpointer value, gpointer new_table)
253 {
254 struct perfcounter *newperf;
255
256 newperf = g_new0(struct perfcounter, 1);
257 newperf->count = ((struct perfcounter *) value)->count;
258 newperf->visible = ((struct perfcounter *) value)->visible;
259 newperf->sort = ((struct perfcounter *) value)->sort;
260 g_hash_table_insert((GHashTable *) new_table, strdup(key), newperf);
261 }
262
263 void rotate_perfcounter() {
264 int i;
265 struct processtop *tmp;
266 for (i = 0; i < lttngtop.process_table->len; i++) {
267 tmp = g_ptr_array_index(lttngtop.process_table, i);
268 g_hash_table_foreach(tmp->perf, reset_perf_counter, NULL);
269 }
270 }
271
272 void cleanup_processtop()
273 {
274 gint i, j;
275 struct processtop *tmp;
276 struct files *tmpf; /* a temporary file */
277
278 for (i = 0; i < lttngtop.process_table->len; i++) {
279 tmp = g_ptr_array_index(lttngtop.process_table, i);
280 tmp->totalcpunsec = 0;
281 tmp->threadstotalcpunsec = 0;
282 tmp->fileread = 0;
283 tmp->filewrite = 0;
284
285 for (j = 0; j < tmp->process_files_table->len; j++) {
286 tmpf = g_ptr_array_index(tmp->process_files_table, j);
287 if (tmpf != NULL) {
288 tmpf->read = 0;
289 tmpf->write = 0;
290 }
291 }
292 }
293 }
294
295 struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end)
296 {
297 gint i, j;
298 unsigned long time;
299 struct lttngtop *dst;
300 struct processtop *tmp, *tmp2, *new;
301 struct cputime *tmpcpu, *newcpu;
302 struct files *tmpfile, *newfile;
303
304 dst = g_new0(struct lttngtop, 1);
305 dst->start = start;
306 dst->end = end;
307 dst->process_table = g_ptr_array_new();
308 dst->files_table = g_ptr_array_new();
309 dst->cpu_table = g_ptr_array_new();
310 dst->perf_list = g_hash_table_new(g_str_hash, g_str_equal);
311
312 rotate_cputime(end);
313
314 g_hash_table_foreach(lttngtop.perf_list, copy_perf_counter, dst->perf_list);
315 for (i = 0; i < lttngtop.process_table->len; i++) {
316 tmp = g_ptr_array_index(lttngtop.process_table, i);
317 new = g_new0(struct processtop, 1);
318
319 memcpy(new, tmp, sizeof(struct processtop));
320 new->threads = g_ptr_array_new();
321 new->comm = strdup(tmp->comm);
322 new->process_files_table = g_ptr_array_new();
323 new->perf = g_hash_table_new(g_str_hash, g_str_equal);
324 g_hash_table_foreach(tmp->perf, copy_perf_counter, new->perf);
325
326 /* compute the stream speed */
327 if (end - start != 0) {
328 time = (end - start) / NSEC_PER_SEC;
329 new->fileread = new->fileread/(time);
330 new->filewrite = new->filewrite/(time);
331 }
332
333 for (j = 0; j < tmp->process_files_table->len; j++) {
334 tmpfile = g_ptr_array_index(tmp->process_files_table, j);
335
336 newfile = malloc(sizeof(struct files));
337
338 if (tmpfile != NULL) {
339 memcpy(newfile, tmpfile, sizeof(struct files));
340 newfile->name = strdup(tmpfile->name);
341 newfile->ref = new;
342 g_ptr_array_add(new->process_files_table,
343 newfile);
344 g_ptr_array_add(dst->files_table, newfile);
345 } else {
346 g_ptr_array_add(new->process_files_table, NULL);
347 g_ptr_array_add(dst->files_table, NULL);
348 }
349 /*
350 * if the process died during the last period, we remove all
351 * files associated with if after the copy
352 */
353 if (tmp->death > 0 && tmp->death < end) {
354 g_ptr_array_remove(tmp->process_files_table, tmpfile);
355 g_free(tmpfile);
356 }
357 }
358 g_ptr_array_add(dst->process_table, new);
359
360 /*
361 * if the process died during the last period, we remove it from
362 * the current process list after the copy
363 */
364 if (tmp->death > 0 && tmp->death < end) {
365 g_ptr_array_remove(lttngtop.process_table, tmp);
366 /* FIXME : TRUE does not mean clears the object in it */
367 g_ptr_array_free(tmp->threads, TRUE);
368 free(tmp->comm);
369 g_ptr_array_free(tmp->process_files_table, TRUE);
370 /* FIXME : clear elements */
371 g_hash_table_destroy(tmp->perf);
372 g_free(tmp);
373 }
374 }
375 rotate_perfcounter();
376
377 for (i = 0; i < lttngtop.cpu_table->len; i++) {
378 tmpcpu = g_ptr_array_index(lttngtop.cpu_table, i);
379 newcpu = g_new0(struct cputime, 1);
380 memcpy(newcpu, tmpcpu, sizeof(struct cputime));
381 newcpu->perf = g_hash_table_new(g_str_hash, g_str_equal);
382 g_hash_table_foreach(tmpcpu->perf, copy_perf_counter, newcpu->perf);
383 /*
384 * note : we don't care about the current process pointer in the copy
385 * so the reference is invalid after the memcpy
386 */
387 g_ptr_array_add(dst->cpu_table, newcpu);
388 }
389 /* FIXME : better algo */
390 /* create the threads index if required */
391 for (i = 0; i < dst->process_table->len; i++) {
392 tmp = g_ptr_array_index(dst->process_table, i);
393 if (tmp->pid == tmp->tid) {
394 for (j = 0; j < dst->process_table->len; j++) {
395 tmp2 = g_ptr_array_index(dst->process_table, j);
396 if (tmp2->pid == tmp->pid) {
397 tmp2->threadparent = tmp;
398 g_ptr_array_add(tmp->threads, tmp2);
399 }
400 }
401 }
402 }
403
404 // update_global_stats(dst);
405 cleanup_processtop();
406
407 return dst;
408 }
409
This page took 0.042071 seconds and 4 git commands to generate.