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