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